/*
 * Copyright 2012 The Netty Project
 *
 * The Netty Project licenses this file to you under the Apache License,
 * version 2.0 (the "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at:
 *
 *   https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
 */
package com.baiyu.netty;

import cn.hutool.core.io.FileUtil;
import com.baiyu.model.base.MocProtocolModel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
import java.io.File;
import java.io.RandomAccessFile;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Component
@Scope("prototype")
@ChannelHandler.Sharable
public class MocServerHandler extends SimpleChannelInboundHandler<MocProtocolModel.MocProtocol> {
    @Value("${deviceLogPath}")
    private String deviceLogPath;
    // 存储文件分片（key：fileId，value：文件写入流 + 已接收分片序号）
    private Map<String, FileWriteContext> fileContextMap = new ConcurrentHashMap<>();

    @Override
    public void channelRead0(ChannelHandlerContext ctx, MocProtocolModel.MocProtocol mocProtocol) throws Exception {
        log.debug("接收到了---------------{}--------{}",mocProtocol,mocProtocol.getHomeData().getPersonList());
        doTask(ctx,mocProtocol);
    }

    /**
     * 任务方法
     * @param ctx
     * @param mocProtocol
     */
    private void doTask(ChannelHandlerContext ctx, MocProtocolModel.MocProtocol mocProtocol) throws Exception {
        switch (mocProtocol.getOp()){
            case CreateHome: //测试用
                log.error("---------------进来了---------------");
                byte[] bytes = mocProtocol.getStream().toByteArray();
                // 3. 二进制流解析为 BufferedImage（Hutool 识别前提）
//                ByteArrayInputStream inputStream = new ByteArrayInputStream(bytes);
                log.error("---------------文件长度---------------{}",bytes.length);
//                String code = QrCodeUtil.decode(inputStream);
//                if(code==null){
//                    log.error("-----------------无法识别的二维码------------------");
//                }else{
//                    log.error("-----------------识别出二维码------------------{}",code);
//                    MocProtocol build = MocProtocol.newBuilder()
//                            .setOp(OpKey.QrCode)
//                            .setDevice(mocProtocol.getDevice())
//                            .setData(code)
//                            .build();
//                    ctx.writeAndFlush(build);
//                }
//                inputStream.close();
//                QrCodeUtil.decode()
                MocProtocolModel.MocProtocol w = MocProtocolModel.MocProtocol.newBuilder()
                        .setOp(MocProtocolModel.OpKey.ShowDice0)
                        .build();
                ctx.writeAndFlush(w);

                MocProtocolModel.MocProtocol w1 = MocProtocolModel.MocProtocol.newBuilder()
                        .setOp(MocProtocolModel.OpKey.ShowDice1)
                        .build();
                ctx.writeAndFlush(w1);

                MocProtocolModel.MocProtocol w2 = MocProtocolModel.MocProtocol.newBuilder()
                        .setOp(MocProtocolModel.OpKey.ShowDice2)
                        .build();
                ctx.writeAndFlush(w2);


                MocProtocolModel.MocProtocol ww = MocProtocolModel.MocProtocol.newBuilder()
                        .setOp(MocProtocolModel.OpKey.CommonPlay)
                        .build();
                ctx.writeAndFlush(ww);
//                GameUserModel gm = new GameUserModel();
//                gm.setCtx(ctx);
//                gm.setRoleKey("CS");
//                GameUtil.showImageAfterNSecond("HUANXI",gm,1);
//                GameUtil.showImageAfterNSecond("FENNU",gm,2);
//                GameUtil.showImageAfterNSecond("DAKESHUI",gm,3);
                break;
            case InitDevice:
                log.debug("---------------初始化设备---------------{}-----{}",ctx,mocProtocol);

                log.debug("----------------------初始化设备回应-------------------------{}",mocProtocol);
                ctx.writeAndFlush(mocProtocol);
                break;
            case AttitudeState:
                log.debug("--------------------------接收到了当前玩家摇动作------------------------------{}",mocProtocol);

                break;
            case DeviceLog:
                log.debug("--------------------------接收到了设备日志操作------------------------------{}",mocProtocol);
                long l = System.currentTimeMillis();
                FileUtil.writeString(mocProtocol.getData(),deviceLogPath+"/"+mocProtocol.getDevice()+"/"+l+".txt","UTF-8");
//                FileUtil.writeBytes(mocProtocol.getStream().toByteArray(),deviceLogPath+"/"+mocProtocol.getDevice()+"/"+l+".dat");
                break;
            case BlueToothCut:
                log.debug("--------------------------蓝牙设备断开------------------------------{}",mocProtocol);
                break;
            case BigFileMeta:
                log.debug("--------------------------大文件传输元数据------------------------------");
                // 接收文件元信息，创建文件写入流
                MocProtocolModel.FileMeta meta = mocProtocol.getFileMeta();
                long ll = System.currentTimeMillis();
//                FileUtil.writeString(mocProtocol.getData(),deviceLogPath+"/"+mocProtocol.getDevice()+"/"+l+".txt","UTF-8");
//                FileUtil.writeBytes(mocProtocol.getStream().toByteArray(),deviceLogPath+"/"+mocProtocol.getDevice()+"/"+l+".dat");
                String fileId = meta.getFileId();
                String filep =deviceLogPath+"/"+mocProtocol.getDevice();
                FileUtil.mkdir(filep);
                File saveFile = new File(filep+"/"+ll+".dat");
                RandomAccessFile raf = new RandomAccessFile(saveFile, "rw");
                fileContextMap.put(fileId, new FileWriteContext(raf,meta.getTotalChunks(),meta.getChunkSize()));
                log.debug("---------开始接收文件：" + meta.getFileName() + "（fileId：" + fileId + "）-------");
                break;
            case BigFileChunk:
                log.debug("--------------------------大文件传输chunk------------------------------");
                // 接收分片，按序号写入文件
                MocProtocolModel.FileChunk chunk = mocProtocol.getFileChunk();
                String fileIdc = chunk.getFileId();
                FileWriteContext context = fileContextMap.get(fileIdc);
                if (context == null) {
                    throw new Exception("未收到文件元信息，无法处理分片：" + fileIdc);
                }
                // 按分片序号定位写入位置
                long writePosition = (long) chunk.getChunkIndex() * context.getChunkSize();
                context.getRaf().seek(writePosition);
                // 写入分片数据
                context.getRaf().write(chunk.getChunkData().toByteArray());
                log.debug("-----------接收分片 ---------" + chunk.getChunkIndex() + "（fileId：" + fileIdc + "）---------");
                // 若为最后一个分片，关闭流并移除上下文
                if (chunk.getIsLastChunk()) {
                    context.getRaf().close();
                    fileContextMap.remove(fileIdc);
                    log.debug("---------文件接收完成：{}-------------" ,fileIdc);
                }
        }
    }

    /**
     * 蓝牙断开方法
     * @param mocProtocol
     */
    private void doCloseBlue(MocProtocolModel.MocProtocol mocProtocol) {
        //游戏人从房间里删除

        log.debug("-------------------------------蓝牙断开删除游戏用户--------------------------------------");
    }

    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent event = (IdleStateEvent) evt;
            if (event.state() == IdleState.READER_IDLE) {
                log.debug("30秒没有读到要客户端的信息读空闲，主动断开连接---{}",ctx);
                ctx.close();
            } else if (event.state() == IdleState.WRITER_IDLE) {
                MocProtocolModel.MocProtocol beat = MocProtocolModel.MocProtocol.newBuilder().setOp(MocProtocolModel.OpKey.PING).build();
                log.debug("发送心跳包---{}",beat);
                ctx.writeAndFlush(beat);
            }
        } else {
            super.userEventTriggered(ctx, evt);
        }
    }
    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) {
        log.debug("---------------读完成了-----------------{}",ctx);
        ctx.flush();
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
        log.error("---------------发生异常-----------------",cause);
        //如果房间的用户数这0删除这个房间

        ctx.close();
    }
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        log.error("----------------关闭连接---------------{}",ctx);
        clearconn(ctx);
    }

    private void clearconn(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        //清理设备池
    }

    /**
     * 封装文件写入上下文（流 + 总分片数）
     */
    private class FileWriteContext {
        private RandomAccessFile raf;
        private int totalChunks;
        private int chunkSize;

        public FileWriteContext(RandomAccessFile raf, int totalChunks, int chunkSize) {
            this.raf = raf;
            this.totalChunks = totalChunks;
            this.chunkSize = chunkSize;
        }

        public int getChunkSize() {
            return chunkSize;
        }

        public void setChunkSize(int chunkSize) {
            this.chunkSize = chunkSize;
        }

        public int getTotalChunks() {
            return totalChunks;
        }

        public void setTotalChunks(int totalChunks) {
            this.totalChunks = totalChunks;
        }

        public RandomAccessFile getRaf() {
            return raf;
        }

        public void setRaf(RandomAccessFile raf) {
            this.raf = raf;
        }
    }
}
